
/********************************************************************
 *  Interface Grafica                           Google-PRIG   TP1                   
 * 																    
 *   Grupo:    Carlos Bentes                                        
 *             Fernando Henrique  
 * 
 *                                        03/04/2008  
 ********************************************************************/

#include <iostream>
#include <sstream>
#include <vector>
#include <X11/Xlib.h>   /* defines common Xlib functions and structs. */
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <string>

#include "TpGrafico.h"
#include "uRISC_Beta.h"


TpGrafico::TpGrafico(){
	
	display_name = getenv("DISPLAY");
	/* open connection with the X server. */
	
	display = XOpenDisplay(display_name);
	
	if (display == NULL) {
	    cout << "cannot connect to X server: "<< display_name << endl;
	    exit(1);
	}	
	
	screen_num = DefaultScreen(display);
	display_width = DisplayWidth(display, screen_num);
	display_height = DisplayHeight(display, screen_num);
	
	//dimenciona janela
	width = 550;
	height = 300; 
	
	//verifica se cabe
	if( (display_width <  width) || (display_height <  height) ){
	    cout << "Formato do display nao apropriado." << endl;
	    exit(1);
	}
	
//	cout << "Largura: "<< width << endl;
//	cout << "Altura:  " << height << endl;
	passoCiclo = 0;
	
	XSync(display, False);
	//inicializa reg de uso do tpGrafico
	pcREG = 0;
	irREG = 0;
	regA = 0;
	regB = 0;
	regALU = 0;
	for(int ir=0; ir<8; ir++) setRegREG( 0 ,ir);
	flag_N = 0;
	flag_Z = 0;
	flag_NZ = 0;
	flag_C = 0;
	flag_O = 0;
	flag_T = 0;
	viewOP = 0;
}

TpGrafico::~TpGrafico(){
	  XFreeGC(display, gc);
	  XCloseDisplay(display);
}

void TpGrafico::init(){
	create_simple_window(0,0);
	create_gc();
	//Inicia fonte para escrita
	font_name = "*-12-*";
	/* try to load the given font. */
	font_info = XLoadQueryFont(display, font_name);
	if (!font_info) {
	    cout << "XLoadQueryFont: failed loading font " << font_name << endl;
	    exit(-1);
	}	
	/* assign the given font to our GC. */
	XSetFont(display, gc, font_info->fid);
	
	if (display == NULL) {
	    cout << "cannot connect to X server: "<< display_name << endl;
	    exit(1);
	}	
	  XStoreName(display, win, "TP1_uRISC - Google-PRIG 2008"); //titulo da janela
	  XFlush(display);
}

void TpGrafico::create_simple_window(int x, int y){
//	int screen_num = DefaultScreen(display);
	int win_border_width = 2;
	win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
	                            x, y, width, height, win_border_width,
	                            BlackPixel(display, screen_num),
	                            WhitePixel(display, screen_num));
	/* make the window actually appear on the screen. */
	XMapWindow(display, win);
	/* flush all pending requests to the X server. */
}

void TpGrafico::create_gc(){		
	  unsigned long valuemask = 0;			
	  unsigned int line_width = 2;		
	  int line_style = LineSolid;		
	  int cap_style = CapButt;		
	  int join_style = JoinBevel;	
	  
	  gc = XCreateGC(display, win, valuemask, &values);
	  if (gc < 0) {
		fprintf(stderr, "XCreateGC: \n");
	  }
	  /* allocate foreground and background colors for this GC. */
	  XSetForeground(display, gc, BlackPixel(display, screen_num));	
	  XSetBackground(display, gc, WhitePixel(display, screen_num));
	  /* define the style of lines that will be drawn using this GC. */
	  XSetLineAttributes(display, gc, line_width, line_style, cap_style, join_style);
	  /* define the fill style for the GC. to be 'solid filling'. */
	  XSetFillStyle(display, gc, FillSolid);
	  
	  gc2 = XCreateGC(display, win, valuemask, &values);
	  if (gc2 < 0) {
		fprintf(stderr, "XCreateGC: 2 \n");
	  }
	  XSetForeground(display, gc2, WhitePixel(display, screen_num));	
	  XSetBackground(display, gc2, BlackPixel(display, screen_num));
	  XSetLineAttributes(display, gc2, line_width, line_style, cap_style, join_style);
	  XSetFillStyle(display, gc2, FillSolid);
}


void TpGrafico::event(uRISC_Beta &uCPU){
	 /* perform an events loop */
	  {
	    int done = 0;
	    XEvent an_event;
	    KeySym key;
	    char text[255];
	    
	    XSelectInput(display, win, ExposureMask | KeyPressMask |StructureNotifyMask);   
	    
	    bool doneCPU = false;
	    
	    while (!done) {
	    	
	      XNextEvent(display, &an_event);
//	      cout << "Evento: " << an_event.type << endl;
	      
	      switch (an_event.type) {
	        case Expose:
	          /* redraw our window. */
	          tpTela();
	          break;
	          
	        case ConfigureNotify:
	          /* update the size of our window, for expose events. */
	          width = an_event.xconfigure.width;
	          height = an_event.xconfigure.height;
	          break;

	        case KeyPress:
	        	//Le a tecla
	        	if( XLookupString(&an_event.xkey,text,255,&key,0) ==1 ){
	    			if (text[0]=='q') {
	    				done = 1;
	    			}else if(text[0]=='e'){
	    			// executa ciclo
	    			      if(!doneCPU){
	    			    	  switch(passoCiclo){
		    			    	  case 0:
		    			    		  uCPU.ciclo_IF();
		    			    		  break;
		    			    	  case 1:
		    			    		  uCPU.ciclo_ID();
		    			    		  break;
		    			    	  case 2:
		    			    		  uCPU.ciclo_ExMem();
		    			    		  break;
		    			    	  case 3:
		    			    		  uCPU.ciclo_WB();
		    			    		  break;
		    			    	  default: passoCiclo = 0;
	    			    	  }
	    			    	  passoCiclo = (++passoCiclo) % 4;
	    					  if( uCPU.getRegIR() == 0x2fff ) doneCPU = true;  //HALT
					  		  //atualiza info para tpGrafico
	    					  tpAtualizaTela(uCPU);
	    			      }	
	    			}else if(text[0]=='r'){
	    			      // executa ciclo at� o final
	    			      if(!doneCPU){
	    			    	  switch(passoCiclo){
		    			    	  case 0:  uCPU.ciclo_IF();
		    			    	  case 1:  uCPU.ciclo_ID();
		    			    	  case 2:  uCPU.ciclo_ExMem();
		    			    	  case 3:  uCPU.ciclo_WB();
		    			    	  default: passoCiclo = 0;
	    			    	  }
	    					  if( uCPU.getRegIR() == 0x2fff ) doneCPU = true;  //HALT
					  		  //atualiza info para tpGrafico
	    					  tpAtualizaTela(uCPU);
	    			      }				
	    			}else if(text[0]=='m'){
	    				u16bits iniPos;
	    				u16bits fimPos;
	    				cout << endl << "Digite pos inicial: ";
	    				cin >> hex >> iniPos;
	    				cout << "Digite pos final: ";
	    				cin >> hex >> fimPos;
	    				
	    				while(iniPos <= fimPos){
	    					cout << "memRAM[0x" << hex << iniPos << "] = 0x";
	    					cout << hex << uCPU.getMemRAM(iniPos) << endl;
	    					iniPos ++;
	    				}
	    				
	    			}else if(text[0]==' ') comandos();
	        	}
	        	break;
	        	
	        default: 
	        	break;
	      } /* end switch on event type */
	    } /* end while events handling */
	    
	    if(!doneCPU) cout << "[Alerta] Programa nao executado completamente..." << endl << endl;
	    
	  }
}


void TpGrafico::tpClearTela(){
  	XFillRectangle(display, win, gc2, 0, 0, width, height);
}



void TpGrafico::tpAtualizaTela(uRISC_Beta &uCPU){
	  setpcREG(uCPU.getRegPC());  //PC
	  setirREG( uCPU.getRegIR()); //IR
	  for(int ir=0; ir<8; ir++) setRegREG(uCPU.getRegFile(ir),ir);  //Regs
	  setFlagN(((uCPU.getRegFlags() & 0x20) ? 1 : 0));  // Flag N
	  setFlagZ(((uCPU.getRegFlags() & 0x10) ? 1 : 0));  // Flag Z
	  setFlagNZ(((uCPU.getRegFlags() & 0x08) ? 1 : 0)); // Flag NZ
	  setFlagC(((uCPU.getRegFlags() & 0x04) ? 1 : 0));  // Flag C
	  setFlagO(((uCPU.getRegFlags() & 0x02) ? 1 : 0));  // Flag O
	  setFlagT(((uCPU.getRegFlags() & 0x01) ? 1 : 0));  // Flag T
	  setRegA(uCPU.getRegA());
	  setRegB(uCPU.getRegB());
	  setRegALU(uCPU.getRegALU());
	  viewOP = uCPU.getViewOp();
	  tpTela();
}


void TpGrafico::dCaixa(){
	 XDrawRectangle(display, win, gc, 120, 150, 50, 60);
}


void TpGrafico::dReg(int x, int y, string vReg){
	int string_width;
	int font_height;
	string maxSizeStrReg = "RR: 0xFFFF";
	font_height = font_info->ascent + font_info->descent;
	string_width = XTextWidth(font_info, maxSizeStrReg.c_str(), strlen(maxSizeStrReg.c_str()));
	XDrawString(display, win, gc, x, y, vReg.c_str(), strlen(vReg.c_str()));
	//pos_x, pos_y, size_x, size_y
	XDrawRectangle(display, win, gc, x-10, y-font_height-2, string_width+20, font_height+10);
}


void TpGrafico::dRegDSize(int x, int y, string vReg){
	int string_width;
	int font_height;
	font_height = font_info->ascent + font_info->descent;
	string_width = XTextWidth(font_info, vReg.c_str(), strlen(vReg.c_str()));
	XDrawString(display, win, gc, x, y, vReg.c_str(), strlen(vReg.c_str()));
	//pos_x, pos_y, size_x, size_y
	XDrawRectangle(display, win, gc, x-10, y-font_height-2, string_width+20, font_height+10);
}


void TpGrafico::dRegFile(int x, int y){
	int desloca;
	desloca = font_info->ascent + font_info->descent + 10;
	//imprime 8 registradores, R0 - R7
	for(int i = 0; i<=7; i++){
		stringstream ss;
		ss << "R" << i << ": 0x" << hex << regREG[i];
		dReg(x,y + i*desloca, ss.str());
	}
}


void TpGrafico::dpassoURISC(int x, int y){
	int font_height = font_info->ascent + font_info->descent;
	string passoStr_0 = "   IF   ";   //todos do mesmo tamanho
	string passoStr_1 = "   ID   ";
	string passoStr_2 = " Ex/Mem ";
	string passoStr_3 = "   WB   ";
	int string_width = XTextWidth(font_info, passoStr_0.c_str(), strlen(passoStr_0.c_str()));
	XDrawString(display, win, gc, x + 0*string_width, y, passoStr_0.c_str(), strlen(passoStr_0.c_str()));
	XDrawString(display, win, gc, x + 1*string_width, y, passoStr_1.c_str(), strlen(passoStr_1.c_str()));
	XDrawString(display, win, gc, x + 2*string_width, y, passoStr_2.c_str(), strlen(passoStr_2.c_str()));
	XDrawString(display, win, gc, x + 3*string_width, y, passoStr_3.c_str(), strlen(passoStr_3.c_str()));
	//pos_x, pos_y, size_x, size_y
	XDrawRectangle(display, win, gc, (x) + passoCiclo*string_width , y-font_height-2, string_width, font_height+10);
	
}


void TpGrafico::dRegIR(int x, int y){
	stringstream ss;
	ss << "IR: 0x" << hex << irREG;
	dReg(x,y,ss.str());
}

void TpGrafico::dRegPC(int x, int y){
	stringstream ss;
	ss << "PC: 0x" << hex << pcREG;
	dReg(x,y,ss.str());
}

void TpGrafico::dRegFR(int x, int y){
	stringstream ss;
	ss << "FR: N[" << flag_N << "] Z[" << flag_Z <<"] NZ[" << flag_NZ << "] C[" << flag_C << "] O["<< flag_O << "] T["<< flag_T << "]";
	dRegDSize(x,y,ss.str());
}


void TpGrafico::tpTela(){
	//apaga tela
	tpClearTela();
	//gera tela atualizada
	dMemROM(50,50);
	dULA(200,100);
	dRegFile(430,40);
	dRegPC(50,180);
	dRegIR(50,210);
	dRegFR(50,240);
	dpassoURISC(350, 250);  // indica passo: IF, ID, EX/Mem WB
}


void TpGrafico::setVROMPushBack(string str){
	vROM.push_back(str);
}



void TpGrafico::dMemROM(int x, int y){
	int pc = pcREG;
	int string_width;
	int font_height;
	string maxSizeStrRam = "0xFFFF : 0xFFFF";
	
	//calculos relativos ao tamanho da fonte
	font_height = font_info->ascent + font_info->descent;
	string_width = XTextWidth(font_info, maxSizeStrRam.c_str(), strlen(maxSizeStrRam.c_str()));
	// cria titulo
	char* titulo_rom = " Addr.    Data ";
	XDrawRectangle(display, win, gc, x-10, y-2*(font_height)-2, string_width+20,font_height);
	XDrawString(display, win, gc, x, y-font_height-4 ,titulo_rom, strlen(titulo_rom));
    // Cria regiao para 7 posicoes de memoria na tela
	XDrawRectangle(display, win, gc, x-10, y-font_height-2, string_width+20, 7*(font_height)+10 );
	//imprime nas 7 pos
	for(unsigned int i=0; i<7; i++){
		if( (int)(i+pc-3) < 0) continue;
		if(i+pc-3 >= vROM.size()) break;
		XDrawString(display, win, gc, x, y + i*(font_height), vROM[i+pc-3].c_str(), strlen(vROM[i+pc-3].c_str()));
	}	
	XDrawString(display, win, gc, x-8, y + 3*(font_height),">", strlen(">"));
}

void TpGrafico::dULA(int x, int y){
	int d = 51;
	int desloca = font_info->ascent + font_info->descent;
	XPoint points[] = {
	  {x, y},
	  {x+d, y},
	  {x+d+d/3, y+d/3},
	  {x+d+2*d/3, y},
	  {x+2*d+2*d/3, y},
	  {x+d/2+d/3+d, y+3*d/2},
	  {x+d/2+d/3, y+3*d/2},
	  {x, y}
	};
	int npoints = sizeof(points)/sizeof(XPoint);
	XDrawLines(display, win, gc, points, npoints, CoordModeOrigin);
	XDrawString(display, win, gc, x+5*d/4, y+d-desloca/2,"ALU", strlen("ALU"));

	stringstream ssa, ssb, ssc, ssop;
	
	ssop << "OP: 0x" << hex << viewOP;
	ssa << " A: 0x" << hex << regA;
	ssb << " B: 0x" << hex << regB;
	ssc << " C: 0x" << hex << regALU;
	
	string _ssop = ssop.str();
	string _ssa = ssa.str();
	string _ssb = ssb.str();
	string _ssc = ssc.str();
	
	//OP:  A:  B:  C:
	XDrawString(display, win, gc, x+2*d+2*d/3, y+d/2+0*desloca, _ssop.c_str(), strlen(_ssop.c_str()));
	XDrawString(display, win, gc, x+2*d+2*d/3, y+d/2+1*desloca, _ssa.c_str(), strlen(_ssa.c_str()));
	XDrawString(display, win, gc, x+2*d+2*d/3, y+d/2+2*desloca, _ssb.c_str(), strlen(_ssb.c_str()));
	XDrawString(display, win, gc, x+2*d+2*d/3, y+d/2+3*desloca, _ssc.c_str(), strlen(_ssc.c_str()));
}


